﻿#define  _CRT_SECURE_NO_WARNINGS











// 引用全，inline内联函数






#include <iostream>
using namespace std;


// 引用就是同一块空间的多个名字
//int main()
//{
//	int a = 0;
//	int& b = a;
//	int& c = b;
//
//	// C++的引用从始至终，不能改变引用的对象
//	cout << &a << endl;
//	cout << &b << endl;
//	cout << &c << endl;
//
//
//	return 0;
//}


// 引用的特点：
// 1.必须在定义的时候初始化。
// 2.一个变量可以有多个引用。
// 3.引用一旦引用了一个实体，再不能应用其他实体。
//  
//


// 引用的使用场景
// 
// 
// 1.输出型参数
// 
// 使用引用和指针，都能解决的问题叫做  输出型参数。
// 本质：形参的改变想要改变实参	
// 
// 
// 
// 
// 需要通过形参改变实参  ---  使用引用
// 引用可以使用在形参，用来接收实参的值。
//
// 二叉树的构建及遍历。
// https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef?tpId=60&&tqId=29483&rp=1&ru=/activity/oj&qru=/ta/tsing-kaoyan/question-ranking
// 
// 
// 

//#include <stdio.h>
//#include <stdlib.h>
//#include <assert.h>
//
//
//// 树的结构
//typedef struct TreeNode
//{
//	char val;
//	struct TreeNode* left;
//	struct TreeNode* right;
//}TN;
//
//
//
//
//// 通过前序数组，创建树
//TN* rebulidTree(char* a, int* pi)
//{
//	// 当前节点应该为空节点NULL
//	if (a[*pi] == '#')
//	{
//		(*pi)++;
//		return NULL;
//	}
//
//	// 创建该节点
//	TN* root = (TN*)malloc(sizeof(TN));
//	root->val = a[*pi];
//	(*pi)++;
//
//	root->left = rebulidTree(a, pi);
//	root->right = rebulidTree(a, pi);
//
//	return root;
//
//}
// 
// 
// 可以通过 引用 改造为
// TN* rebulidTree(char* a, int& pi)
//{
//	// 当前节点应该为空节点NULL
//	if (a[pi] == '#')
//	{
//		pi++;
//		return NULL;
//	}
//
//	// 创建该节点
//	TN* root = (TN*)malloc(sizeof(TN));
//	root->val = a[pi];
//	pi++;
//
//	root->left = rebulidTree(a, pi);
//	root->right = rebulidTree(a, pi);
//
//	return root;
//
//}
// 








// 2.引用做返回值
// 
// 
// 
// 
// 第一种：
// 
// 
// 
//int Count()
//{
//	int n = 0;// n变量在Count栈区中保存
//	n++;
//
//	return n;// 子函数的返回值，在返回过程中，需要临时存储起来。
//			 // 当返回值比较小，一般用寄存器存储该临时变量。
//			 // 当返回值比较大，会在被调用函数销毁前，提前会在调用该函数的函数栈帧中开辟并存储起来
//			 // 当该函数调用完成并销毁后，再把这些临时保存的返回值，赋值给调用函数的返回值接收变量中。
//}
//
//int main()
//{
//	int ret = Count();
//
//	return 0;
//}








// 
// 第二种：
// 
// 
// 
// 注意：即使n存储在静态区中，返回值函数需要先找临时空间进行存储，之后再赋值给接收函数返回值的具体变量
// 
// 
// 传值返回
// 
//int Count()
//{
//	static int n = 0;// n变量在静态区中保存
//	n++;
//
//	return n;// 子函数的返回值，在返回过程中，需要临时存储起来。
//	// 当返回值比较小，一般用寄存器存储该临时变量。
//	// 当返回值比较大，会在被调用函数销毁前，提前会在调用该函数的函数栈帧中开辟并存储起来
//	// 当该函数调用完成并销毁后，再把这些临时保存的返回值，赋值给调用函数的返回值接收变量中。
//}
//
//int main()
//{
//	int ret = Count();
//
//	return 0;
//}
//
// 
// 
// 
// 
// 
// 对于第二种，可以进行优化    ----  return 时不创建临时变量
// 
// 
// 
// 
// 
// 传引用返回（传址返回）
// 
//int& Count()// 返回值的类型为引用类型
//{
//	static int n = 0;// n变量在静态区中保存
//	n++;
//
//	return n;// 由于n变量存储在静态区，不会同函数一起销毁
//			 // 采用传引用返回
//			 //	这里的n在返回过程中，顺便创建了一个n的别名（引用），而这个n变量的别名（引用）作为了返回值。
//}
//
//int main()
//{
//	int ret = Count();
//
//	return 0;
//}
// 
// 
// 
// 注意：
// 出了函数的作用域，这个变量不会和函数一起被销毁，那么可以传引用返回。
// 







// 实际应用：
// 
//#define N 10
//typedef struct Array
//{
//	int a[N];
//	int size;
//}AY;
//
//int& PosAt(AY& ay, int i)
//{
//	return ay.a[i];
//	// 这里引用返回的两重意义：
// 
//						// 1.引用返回，减少了return操作多余的拷贝
//	
//						//  2.引用返回，调用者可以修改返回对象（若为临时变量，临时变量具有常性，不可修改）
//
//}
//
//int main()
//{
//	AY ay;
//
//	for (int i = 0; i < N; i++)
//	{
//		PosAt(ay, i) = i * 10;
//	}
//
//	for (int i = 0; i < N; i++)
//	{
//		cout << PosAt(ay, i) << " ";
//	}
//	cout << endl;
//
//	return 0;
//}






// 若返回值会与被调函数一起销毁，此时 传引用返回，返回值则是未定义状态
// 
//int& Add(int a, int b)
//{
//	int c = a + b;
//	return c;
//}
//int main()
//{
//	int& ret = Add(1, 2);
//	Add(3, 4);
//	cout << "Add(1, 2) is :" << ret << endl;
//	return 0;
//}
// 
// 
// 
// 若使用权已经交还系统，则必须使用传值返回
// 
// 出函数作用域之后，这些东西还在：静态，全局，上一层栈帧的，malloc的。
// 
// 
// 
// 
// 类相当于在结构体的基础上进行拓展。
// 使结构体不仅可以定义变量，还可以定义函数。
// 
// 
// 引用做返回值：本质为面向对象做铺垫
// 
//






// 传值、传引用效率比较
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 传引用调用的效率高
// 
// 
//#include <time.h>
//
//struct A { int a[10000]; };
//void TestFunc1(A a) {}
//void TestFunc2(A& a) {}
//
//void TestRefAndValue()
//{
//	A a;
//	// 以值作为函数参数
//	size_t begin1 = clock();
//	for (size_t i = 0; i < 10000; ++i)
//		TestFunc1(a);
//	size_t end1 = clock();
//
//
//	// 以引用作为函数参数
//	size_t begin2 = clock();
//	for (size_t i = 0; i < 10000; ++i)
//		TestFunc2(a);
//	size_t end2 = clock();
//
//
//
//	// 分别计算两个函数运行结束后的时间
//	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
//	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
//}
//
//int main()
//{
//	TestRefAndValue();
//
//	return 0;
//}
//
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 值和引用的作为返回值类型的性能比较
//
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 传引用返回，效率更高
// 
// 
//
//#include <time.h>
//
//
//struct A { int a[10000]; };
//A a;
//
//// 值返回
//A TestFunc1() { return a; }
//
//// 引用返回
//A& TestFunc2() { return a; }
//
//
//
//void TestReturnByRefOrValue()
//{
//	// 以值作为函数的返回值类型
//	size_t begin1 = clock();
//	for (size_t i = 0; i < 100000; ++i)
//		TestFunc1();
//	size_t end1 = clock();
//	// 以引用作为函数的返回值类型
//	size_t begin2 = clock();
//	for (size_t i = 0; i < 100000; ++i)
//		TestFunc2();
//	size_t end2 = clock();
//	// 计算两个函数运算完成之后的时间
//	cout << "TestFunc1 time:" << end1 - begin1 << endl;
//	cout << "TestFunc2 time:" << end2 - begin2 << endl;
//}
//
//
//int main()
//{
//	TestReturnByRefOrValue();
//
//	return 0;
//}



















// 常引用
// 
//int Count()
//{
//	int a = 1;
//	return a;
//}
//
//int main()
//{
//	int a = 1;
//	int& b = a;
//
//	// 指针和引用，在赋值/初始化，权限可以缩小，但不能放大
//	const int c = 2;// const int 为只读整型， int& 为可读写整型的引用（别名）
//	//int& d = c;
//	const int& d = c;// 权限保持
//
//
//	// 指针同理
//	int e;
//	const int* p1 = &e;
//	//int* p2 = p1;
//	const int* p2 = p1;// 权限保持
//
//
//
//	//int& ret = Count();
//	// 错误：
//	// Count 返回值是int类型，实际上是返回临时变量，临时变量既具有常性不可以被修改，
//	// 给了int& 相当于权限放大，所以不能。
//	const int& ret = Count();// 权限保持
//
//
//
//
//
//
//
//	int i = 0;
//	//double& rd = i;
//	// 显示类型转换：
//	cout << (double)i << endl;
//	// 隐式类型转换：
//	double dd = i;
//	// 错误原因：
//	// 类型转换会产生临时变量，将这个double类型临时变量，给rd double类型的引用，临时变量具有常性，所以需要增加const
//	const double& rd = i;
//	// 正确
//
//
// 
// 
// 
// 
// 
// 
// 
//	return 0;
//}

















// 引用和指针的区别
// 
// 
// 
// 
//  
// 语法上：指针没有开辟空间，指针开辟空间。
// 实际上：引用是用指针去实现的（汇编层面上）
// 
// 引用是更安全的指针。
//
// 
// 
// 
// 
// 
// 
//引用和指针的不同点:
// 
// 
//
// 1. 引用概念上定义一个变量的别名，指针存储一个变量地址。
//
// 2. 引用在定义时必须初始化，指针没有要求
// 
// 3. 引用在初始化时引用一个实体后，就不能再引用其他实体，而指针可以在任何时候指向任何
// 一个同类型实体
//
// 4. 没有NULL引用，但有NULL指针
//
// 5. 在sizeof中含义不同：引用结果为引用类型的大小，但指针始终是地址空间所占字节个数(32
//	位平台下占4个字节)
//
// 6. 引用自加即引用的实体增加1，指针自加即指针向后偏移一个类型的大小
//
// 7. 有多级指针，但是没有多级引用
//
// 8. 访问实体方式不同，指针需要显式解引用，引用编译器自己处理
//
// 9. 引用比指针使用起来相对更安全
















// C++ 中的宏
// 
// 
// 
// 
// 
// 1.宏定义的常量
// C++推荐用const和enum枚举 ，进行替代宏常量。
// 
// 2.宏定义函数
// 推荐用inline去替代宏函数。
// 
// 
// 
// 宏的缺点：
// 
// 1.不能调试
// 2.没有类型安全的检查
// 3.在有些场景下非常复杂
// 
// 

// 实现ADD的宏函数
//#define ADD(x,y) ((x)+(y))
//
//int main()
//{
//	int ret = ADD(1, 2);
//	cout << ret << endl;
//
//	return 0;
//}
// 
// 
// 
// 宏的优点：
// 
// 1.宏常量，提高代码复用性，可维护性。
// 2.宏函数，类型不固定。
// 3.宏函数，不用建立栈帧，运行效率高。
// 
// 
// 
// 
// 
// C++，宏取其精华去其糟粕
// 















// 7. 内联函数
// 
// 
// 
// 
// 
// 
// 
// 
// 
// 
// inline ，按照一个函数去使用，但是不建立函数栈帧
// 
// 内联inline，在默认debug版本下，是不会起作用，为了方便调试。
// 
//inline int Add(int x, int y)
//{
//	return x + y;
//}
//
//int main()
//{
//	int ret = Add(1, 2);
//	cout << ret << endl;
//
//	return 0;
//}
// 
// 
// 
// 
// 
// 
// 
// 
// 内联特点：
// 
// 1.inline内联，是以空间换时间的做法。
// 
// 这里的空间指，编译完成后的可执行程序的大小。
// 
// 
// 
// 举个例子：
// Swap函数，10行。
// 程序调用Swap函数，1000次。
// 
// 不使用inline，
// Swap+调用Swap指令，合计 10+1000 行指令
// 
// 使用inline
// Swap+调用Swap指令，合计 10*1000 行指令
// 
// 
// 
// 
// 
// 2.inline内联，对编译器只是一个建议，编译器自行判断是否采用inline内联。
// 
// 频繁调用的短小函数，增加inline内联。（10行以内）
// 长函数，递归函数，不加inline内联。
// 
//










